package com.tuling.fox._05.jucdemo.jmm;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.ReentrantLock;


/**
 * @author Fox
 */
public class Test {

    // 循环次数
    private static final int LOOP_COUNT = 100;

    private static int syncCount = 0;

    private static int reentrantCount = 0;

    static final Object object = "";

    static ReentrantLock lock = new ReentrantLock();

    static AtomicInteger counter = new AtomicInteger();


    /**
     * 10 * 10000 = 10000000
     */
    public static void main(String[] args) throws InterruptedException {

        long startTime, endTime;

        startTime = System.nanoTime();
        addSynchronized();
        endTime = System.nanoTime();
        System.out.println("Time for addSynchronized: " + (endTime - startTime) / 1_000_000 + " ms");

        // ReentrantLock
        startTime = System.nanoTime();
        addReentrantLock();
        endTime = System.nanoTime();
        System.out.println("Time for addReentrantLock: " + (endTime - startTime) / 1_000_000 + " ms");

        // AtomicInteger
        startTime = System.nanoTime();
        addAtomicInteger();
        endTime = System.nanoTime();
        System.out.println("Time for addAtomicInteger: " + (endTime - startTime) / 1_000_000 + " ms");

        // volatile
        startTime = System.nanoTime();
        addVolatile();
        endTime = System.nanoTime();
        System.out.println("Time for addLongAdder: " + (endTime - startTime) / 1_000_000 + " ms");

    }

    /**
     * Java的内置锁，会导致线程在争抢锁时发生阻塞，尤其是在锁竞争激烈时，会显著降低性能。
     *  - 原因：synchronized 会导致线程频繁进入和退出临界区，导致上下文切换开销。
     */
    public static void addSynchronized() throws InterruptedException {
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < LOOP_COUNT; i++) {
            Thread thread = new Thread(() -> {
                synchronized (object) {
                    for (int j = 0; j < 10000; j++) {
                        syncCount++;
                    }
                }
            });
            threads.add(thread);
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("addSynchronized :sum = " +  syncCount);
    }

    /**
     * ReentrantLock 相比 synchronized，ReentrantLock 提供了更高的灵活性和控制（比如可以尝试锁、加锁时设置超时等），
     * 但其本质上也会导致线程阻塞，只是可以通过一些高级特性（如 tryLock）减少阻塞的影响。ReentrantLock 在竞争较激烈时，也会导致上下文切换和性能下降。
     */
    public static void addReentrantLock() {
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < LOOP_COUNT; i++) {
            Thread thread = new Thread(() -> {
                lock.lock();
                try {
                    for (int j = 0; j < 10000; j++) {
                        reentrantCount++;
                    }
                } finally {
                    lock.unlock();
                }
            });
            threads.add(thread);
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("addReentrantLock :sum = " +  reentrantCount);

    }

    /**
     * 它使用 CAS（Compare-And-Swap）技术来实现原子操作，在高并发环境下，性能表现较好，因为它不会导致线程阻塞。
     * 只要线程竞争不太激烈，它通常会比 synchronized 和 ReentrantLock 更高效。
     * 但在某些情况下，它可能会因为自旋消耗 CPU 资源而降低性能。
     */
    public static void addAtomicInteger() {
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < LOOP_COUNT; i++) {
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    counter.incrementAndGet();
                }
            });
            threads.add(thread);
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("addAtomicInteger :sum = " +  counter);
    }


    // LongAdder 是 JDK8 新添加的类，它提供了更高级的原子操作，如：累加、平均值、最小值、最大值等。
    // 应用场景是非常高并发的计数器更新（例如有大量线程进行自增操作），AtomicInteger 会在高并发时出现性能瓶颈。此时，LongAdder 是一个更适合的选择，它在高并发场景下能够提高性能。
    private static LongAdder volatileCount = new LongAdder();
//    private static volatile int volatileCount = 0;

    /**
     * volatile 可以避免指令重排序，但是不能保证原子性。
     * 只能保证可见性，不能保证原子性，因此在多线程修改时会发生竞态条件，导致结果不正确。
     */
    public static void addVolatile(){
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < LOOP_COUNT; i++) {
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    volatileCount.increment();
                }
            });
            threads.add(thread);
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("addLongAdder :sum = " +  volatileCount);
    }

    // addSynchronized 的执行时间是 2 毫秒，sum 的值为 100000。
    //synchronized 是一种阻塞同步机制，当多个线程访问临界区时，只有一个线程可以执行，其他线程需要等待锁释放。尽管会有线程争用锁的情况，但由于每个线程的执行时间相对较短，synchronized 的执行时间较低。
    //2. addReentrantLock 的执行时间是 3 毫秒，sum 的值为 100000。
    //ReentrantLock 提供了比 synchronized 更灵活的锁机制，但它的底层实现依然需要线程争抢锁。在执行时间上，ReentrantLock 稍微多于 synchronized，但是差距不大。这是因为 ReentrantLock 本质上是一个显示锁，涉及到加锁和释放锁的操作，可能比 synchronized 稍微慢一点，但差距不大。
    //3. addAtomicInteger 的执行时间是 4 毫秒，sum 的值为 100000。
    //AtomicInteger 使用了 CAS（Compare-And-Swap）机制，是一种无锁的原子操作。在高并发下，它通常会比锁机制（synchronized 和 ReentrantLock）更高效，然而，CAS 操作的性能会受到“自旋锁”的影响，在某些情况下它也会有较高的延迟。特别是在高并发且竞争激烈时，CAS 可能会出现自旋，造成一定的性能损耗，因此执行时间略高。
    //4. addVolatile 的执行时间是 14 毫秒，sum 的值为 100000。
    //volatile 保证了对变量的可见性，但并不保证原子性。在你的代码中，volatileCount++ 是一个非原子操作，它相当于先读取变量值，再加 1，最后写回，这就可能导致竞态条件。因此，尽管 volatile 可以避免缓存一致性问题，但由于缺乏同步，它在并发环境下会导致数据不一致，可能增加额外的性能开销。

    //分析原因：
    //synchronized 和 ReentrantLock 虽然会导致线程阻塞和上下文切换，但由于它们确保了线程安全，因此执行的结果是正确的，并且开销相对较小。
    //AtomicInteger 采用无锁的 CAS 操作，在某些高并发情况下，比 synchronized 和 ReentrantLock 的锁竞争开销更低，但依然可能存在一定的延迟（尤其是竞争激烈时，CAS 可能会自旋等待）。
    //volatile 本身不具备原子性，只保证可见性。在并发更新时会导致问题，可能需要更多的时间来处理。

    //进一步优化的思路：
    //AtomicInteger：可以使用 AtomicLong 或 LongAdder（在高并发情况下表现更好），特别是在大量累加的场景中，LongAdder 会比 AtomicInteger 更有效。
    //
    //synchronized 和 ReentrantLock：如果可能，可以考虑使用更细粒度的锁，例如 ReadWriteLock，或者通过减少临界区的长度来减少锁的持有时间。
    //
    //volatile：要确保线程安全，volatile 只能解决可见性问题，不能解决原子性问题。在并发计数的场景下，AtomicInteger 或 LongAdder 更适合。

    //从你提供的执行时间来看，synchronized 和 ReentrantLock 在执行上的差异不大，可能是因为锁的竞争不算很激烈。AtomicInteger 的执行时间略长，原因可能在于 CAS 操作和自旋的开销。
    //volatile 的性能最差，主要是因为缺少原子性保证，导致在并发环境下需要更多的时间处理同步问题。
//    public static void main1(String[] args) {
//
//        for (int i = 0; i < 10; i++) {
//            Thread thread = new Thread(() -> {
//                //synchronized (object) { // 锁竞争激烈，线程阻塞严重，锁竞争严重，性能极低（阻塞就会通过park方法->用户态切换到内核态，高昂的性能代价）
//                //lock.lock(); //
//                //
////                for(;;){
////                    //state=0
////                    if(casLock.getState()==0 && casLock.cas()) {
////                        try {
////                            for (int j = 0; j < 10000; j++) {
////                                sum++;
////                            }
////                            //
////                            System.out.println(casLock.getState());
////                        } finally {
////                            //lock.unlock();
////                            // state=0
////                            casLock.setState(0);
////                        }
////                        break;
////                    }
////                }
//
//                //}
//            });
//            thread.start();
//        }
//
//        try {
//            Thread.sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//
//        System.out.println(sum);
//
//    }


}
